package artificialNeuralNetworks.Demo;

import java.util.LinkedHashSet;

import artificialNeuralNetworks.ANN.ANNDataExtractor;
import artificialNeuralNetworks.ANN.ANNExample;
import artificialNeuralNetworks.ANN.ANNExampleSet;
import artificialNeuralNetworks.ANN.NeuralNetwork;
import decisiontreelearning.DecisionTree.AttributeList;
import decisiontreelearning.DecisionTree.DataCorrupter;
import decisiontreelearning.DecisionTree.DecisionTreeTest;
import decisiontreelearning.DecisionTree.Example;
import decisiontreelearning.DecisionTree.ExampleSet;

/**
 * FileName: TestIris.java
 * @Description:
 * 
 * @author Xunhu(Tiger) Sun
 *         email: TigerSun86@gmail.com
 * @date Mar 9, 2014 8:15:11 AM
 */
public class TestIris {
    private static final String ATTR_FILE_URL =
            "http://cs.fit.edu/~pkc/classes/ml/data/iris-attr.txt";
    private static final String TRAIN_FILE_URL =
            "http://cs.fit.edu/~pkc/classes/ml/data/iris-train.txt";
    private static final String TEST_FILE_URL =
            "http://cs.fit.edu/~pkc/classes/ml/data/iris-test.txt";
    public static void iter (int maxIter) {
        AttributeList attrList =
                DecisionTreeTest.getAttributeList(ATTR_FILE_URL);
        ExampleSet dataSet = DecisionTreeTest.getExampleSet(TRAIN_FILE_URL);
        final ANNExampleSet testSet = getExampleSet(TEST_FILE_URL);
        // Corrupt the train set by given ratio.
        dataSet = DataCorrupter.corrupt(dataSet, attrList, 0.00);
        final ANNExampleSet set = convertDataSet(dataSet);

        final ANNExampleSet trainSet = set;

        final int[] nH = { 3 };
        final NeuralNetwork net = new NeuralNetwork(4, nH, true, 3, true);

        int iter = 0;
        while (iter < maxIter) {
            net.update(trainSet);
            iter++;
        }
        System.out.println("Num of iter: " + iter);

        System.out.println("Final Train accuracy: " + net.evaluate(trainSet));
        System.out.println("Final Test accuracy: " + net.evaluate(testSet));

    }
    public static void crossVal () {
        AttributeList attrList =
                DecisionTreeTest.getAttributeList(ATTR_FILE_URL);
        ExampleSet dataSet = DecisionTreeTest.getExampleSet(TRAIN_FILE_URL);
        final ANNExampleSet testSet = getExampleSet(TEST_FILE_URL);
        // Corrupt the train set by given ratio.
        dataSet = DataCorrupter.corrupt(dataSet, attrList, 0.00);
        final ANNExampleSet set = convertDataSet(dataSet);

        ANNExampleSet[] exArray = set.splitIntoTwoSets(0.667);
        final ANNExampleSet trainSet = exArray[0];
        final ANNExampleSet valSet = exArray[1];

        System.out.println("train : " + trainSet.size() + "val: "
                + valSet.size());
        final int[] nH = { 3 };
        final NeuralNetwork net = new NeuralNetwork(4, nH, true, 3, true);

        double maxAccur = 0;
        NeuralNetwork bestNet = new NeuralNetwork(net);
        int bestIter = 0;

        int iter = 0;
        while (iter < 10000) {
            net.update(trainSet);
            iter++;

            final double accur = net.evaluate(valSet);

            if (Double.compare(maxAccur, accur) < 0) {
                maxAccur = accur;
                bestIter = iter;
                bestNet = new NeuralNetwork(net);
            }

        }
        System.out.println("Num of iter: " + iter);
        System.out.println("Best iter: "+bestIter);

        System.out.println("Final Train accuracy: " + net.evaluate(trainSet));
        System.out.println("Final Validation accuracy: " + net.evaluate(valSet));
        System.out.println("Final Test accuracy: " + net.evaluate(testSet));
        
        System.out.println("Best Train accuracy: " + bestNet.evaluate(trainSet));
        System.out.println("Best Validation accuracy: " + bestNet.evaluate(valSet));
        System.out.println("Best Test accuracy: " + bestNet.evaluate(testSet));

    }
    public static void kFold(){
        AttributeList attrList =
                DecisionTreeTest.getAttributeList(ATTR_FILE_URL);
        ExampleSet dataSet = DecisionTreeTest.getExampleSet(TRAIN_FILE_URL);
        final ANNExampleSet testSet = getExampleSet(TEST_FILE_URL);
        // Corrupt the train set by given ratio.
        dataSet = DataCorrupter.corrupt(dataSet, attrList, 0.00);
        final ANNExampleSet set = convertDataSet(dataSet);
        ANNExampleSet[] exArray = set.splitIntoMultiSets(5);
        int sumIter = 0;
        for (int val = 0; val < exArray.length; val++) {
            final ANNExampleSet valSet = exArray[val];
            final ANNExampleSet trainSet = new ANNExampleSet();
            for (int other = 0; other < exArray.length; other++) {
                if (other != val) { // All other set is train set.
                    trainSet.addAll(exArray[other]);
                }
            }
            System.out.println("train : " + trainSet.size() + "val: "
                    + valSet.size());
            final int[] nH = { 3 };
            final NeuralNetwork net = new NeuralNetwork(4, nH, true, 3, true);
            NeuralNetwork lastNet = new NeuralNetwork(net);
            double lastAccur = 0;
            int iter = 0;
            while (true) {
                net.update(trainSet);
                iter++;

                final double accur = net.evaluate(valSet);
                double dws = net.difference(lastNet);
                lastNet = new NeuralNetwork(net);
                // System.out.printf("%d %.3f %.5f ", iter, accur, dws);
                if (iter % 20 == 0) {
                    // System.out.println();
                }

                if (Double.compare(accur, lastAccur) < 0) {
                    System.out.println("Stop for accur from " + lastAccur
                            + " to " + accur);
                    break; // Accuracy in validation set is going down.
                } else if (dws < 0.001) {
                    System.out.println("No delta weight" + dws);
                    break;
                } else {
                    lastAccur = accur;
                }

            }
            sumIter += iter - 1;
            System.out.println("Num of iter: " + iter);

            System.out.println("Train accuracy: " + net.evaluate(trainSet));
            System.out.println("Validation accuracy: " + net.evaluate(valSet));
            System.out.println("Test accuracy: " + net.evaluate(testSet));
        }
        final int meanIter = sumIter / exArray.length;
        System.out.println("Mean of iter: " + meanIter);
        final int[] nH = { 3 };
        final NeuralNetwork net = new NeuralNetwork(4, nH, true, 3, true);
        for (int iter = 0; iter < meanIter; iter++) {
            net.update(set);
        }
        System.out.println("Train accuracy: " + net.evaluate(set));
        System.out.println("Test accuracy: " + net.evaluate(testSet));
    }
    public static void main (final String[] args) {
        //crossVal();
        iter(10000);
    }

    private static ANNExampleSet convertDataSet (final ExampleSet exSet) {
        final ANNExampleSet aExSet = new ANNExampleSet();
        for (Example e : exSet.getExampleSet()) {
            final ANNExample ae = new ANNExample();
            for (int i = 0; i < e.size() - 1; i++) {
                ae.x.add(Double.parseDouble(e.get(i)));
            }
            final String target = e.get(e.size() - 1);
            if (target.equals("Iris-setosa")) {
                ae.t.add(0.95);
                ae.t.add(0.05);
                ae.t.add(0.05);
            } else if (target.equals("Iris-versicolor")) {
                ae.t.add(0.05);
                ae.t.add(0.95);
                ae.t.add(0.05);
            } else if (target.equals("Iris-virginica")) {
                ae.t.add(0.05);
                ae.t.add(0.05);
                ae.t.add(0.95);
            }
            aExSet.add(ae);
        }
        return aExSet;
    }

    private static ANNExampleSet getExampleSet (final String examFName) {
        final ANNExampleSet exSet = new ANNExampleSet();
        final ANNDataExtractor in = new ANNDataExtractor(examFName);

        while (true) {
            final String line = in.nextLine();
            if (line == null) {
                break;
            }
            // 2 Space
            final String[] examStr = line.split(" ");
            final ANNExample ex = new ANNExample();
            for (int i = 0; i < examStr.length - 1; i++) {
                ex.x.add(Double.parseDouble(examStr[i]));
            }
            final String target = examStr[examStr.length - 1];
            if (target.equals("Iris-setosa")) {
                ex.t.add(0.95);
                ex.t.add(0.05);
                ex.t.add(0.05);
            } else if (target.equals("Iris-versicolor")) {
                ex.t.add(0.05);
                ex.t.add(0.95);
                ex.t.add(0.05);
            } else if (target.equals("Iris-virginica")) {
                ex.t.add(0.05);
                ex.t.add(0.05);
                ex.t.add(0.95);
            }
            exSet.add(ex);
        }

        in.close();

        if (exSet.isEmpty()) {
            System.err.println("No example in: " + examFName);
            return null;
        } else {
            return exSet;
        }
    }
}
